﻿using System;
using System.Data.SqlClient;

namespace DataCenter.Share
{
    [Serializable]
    public unsafe class FragBinary : Binary
    {
        public FragBinary(DateTime startTime, DateTime endTime) 
            : this(1, 1, startTime, endTime) { }
        public FragBinary(int interval, int fieldCount, DateTime startTime, DateTime endTime)
        {
            this.Initialize(interval, fieldCount, startTime, endTime);
        }
        public FragBinary(int interval, int fieldCount, DateTime startTime, DateTime endTime, byte[] buffer)
        {
            this.Initialize(interval, fieldCount, startTime, endTime, buffer);
        }
        public FragBinary(SqlDataReader reader, DateTime startTime, DateTime endTime, int columnIndex)
        {
            this.Initialize(reader, startTime, endTime, columnIndex);
        }

        public override byte[] GetBuffer()
        {
            return buffer;
        }
        public override int GetRecordIndex(DateTime valueTime)
        {
            int timeIndex = GetTimeIndex(valueTime);
            return (timeIndex - StartTimeIndex) * SingleBufferLength;
        }
        public override int GetRecordIndex(int timeIndex)
        {
            return (timeIndex - StartTimeIndex) * SingleBufferLength;
        }

        protected override byte[] CreateBuffer()
        {
            byte[] buffer = new byte[TotalBufferLength];
            fixed (byte* pBuffer = buffer)
            {
                for (int i = 0; i < TotalBufferLength; i++)
                {
                    *(pBuffer + i) = EMPTY_FLAG;
                }
            }
            return buffer;
        }
        protected override int GetRecordCount()
        {
            int recordCount = 0;
            fixed (byte* pBuffer = buffer)
            {
                for (int recordIndex = 0;
                    recordIndex < TotalBufferLength;
                    recordIndex += SingleBufferLength)
                {
                    if (IsRecordExist(pBuffer, recordIndex))
                        recordCount += 1;
                }
            }
            return recordCount;
        }
        protected override DateTime GetTimeFromRecordIndex(int recordIndex)
        {
            int timeIndex = recordIndex / SingleBufferLength - StartTimeIndex;
            return GetTimeFromTimeIndex(timeIndex);
        }

        private void Initialize(int interval, int fieldCount, DateTime startTime, DateTime endTime)
        {
            SetParameters(interval, fieldCount, startTime, endTime);
            buffer = CreateBuffer();
        }
        private void Initialize(int interval, int fieldCount, DateTime startTime, DateTime endTime, byte[] buffer)
        {
            Initialize(interval, fieldCount, startTime, endTime);
            CheckBuffer(buffer);
            this.buffer = buffer;
            base.recordCount = GetRecordCount();
        }
        private void Initialize(SqlDataReader reader, DateTime startTime, DateTime endTime, int columnIndex)
        {
            if (columnIndex >= reader.FieldCount)
                throw new ArgumentOutOfRangeException("索引 ColumnIndex 大于 Reader 的 FieldCount");

            byte[] header = new byte[HEADER_BUFFER_LENGTH];
            reader.GetBytes(columnIndex, 0, header, 0, HEADER_BUFFER_LENGTH);
            
            this.recordCount = BitConverter.ToInt32(header, sizeof(byte));
            this.interval = BitConverter.ToInt32(header, sizeof(byte) + sizeof(int));
            this.fieldCount = BitConverter.ToInt32(header, sizeof(byte) + sizeof(int) * 2);
            SetParameters(interval, fieldCount, startTime, endTime);

            this.Version = header[0];
            this.buffer = new byte[TotalBufferLength];

            int offset = HEADER_BUFFER_LENGTH + StartTimeIndex * SingleBufferLength;
            reader.GetBytes(columnIndex, offset, buffer, 0, TotalBufferLength);
        }
        private void SetParameters(int interval, int fieldCount, DateTime startTime, DateTime endTime)
        {
            ThrowCheck(interval, fieldCount);            

            base.Version = VERSION;
            base.interval = interval;
            base.fieldCount = fieldCount;

            base.StartTimeIndex = GetTimeIndex(startTime);
            base.EndTimeIndex = GetTimeIndex(endTime);
            
            base.SingleBufferLength = sizeof(byte) + sizeof(float) * fieldCount;
            base.HeaderBufferLength = 0;
            base.TotalRecordCount = EndTimeIndex - StartTimeIndex + 1;
            base.MAX_RECORD_COUNT = MINUTES_PER_DAY / interval;
            base.TotalBufferLength = TotalRecordCount * SingleBufferLength;

            CheckRegion(startTime, endTime);
        }
    }
}
